/*
 *  LogStore : store.c
 *                              Hyojun Kim
 */

#include "store.h"

typedef struct _log
{
    struct _log*    next;
    unsigned        key;
    unsigned        length;
    byte*           data; 
} LOG;

/* hash table for fast search */
#define HASHSIZE                (1024)
LOG* ghashtbl[HASHSIZE];

/* search with key by hashing */
static LOG*
_search_log(unsigned key)
{
    LOG* l;

    l = ghashtbl[key % HASHSIZE];
    while (l)
    {
        /* search linked list */
        if (l->key == key) break;
        l = l->next;
    }

    return l;
}

/* generate unique random key */
static unsigned 
_generate_key(void)
{
    unsigned key;

    /* eight dight key */
    do key = rand() % 100000000;
    while (_search_log(key) != NULL);

    return key;
}

/* memory allocation for log data structure */
static LOG*
_new_log(unsigned length, byte* data)
{
    LOG* l;

    l = (LOG*)malloc(sizeof(LOG) + length);
    if (l)
    {
        memset(l, 0, sizeof(LOG));
        l->data = (byte*)l + sizeof(LOG);
    }
    return l;
}

/* hash table initialization */
void
store_init(void)
{
    int idx;

    for (idx = 0; idx < HASHSIZE; idx++)
    {
        ghashtbl[idx] = NULL;
    }
    srand(0);
}

/* put data */
int
store_put(unsigned length, byte* data, unsigned* key)
{
    LOG* l;
    int  idx;
    int  r = -1;

    /* memory allocation */
    l = _new_log(length, data);
    if (l)
    {
        /* copy data to allocated LOG */
        memcpy(l->data, data, length);

        /* fill LOG fields */
        l->length     = length;
        l->key        = _generate_key();
        idx           = l->key % HASHSIZE;
        l->next       = ghashtbl[idx];
        ghashtbl[idx] = l;
        *key          = l->key;

        r = 0;
    }
    return r;
}

/* get data */
int
store_get(unsigned key, unsigned bsize, byte* buffer, unsigned* length)
{
    LOG* l;
    int  r = -1;

    /* search LOG with given key*/
    l = _search_log(key);
    if (l && buffer)
    {
        /* buffer size check */
        *length = (l->length < bsize) ? l->length : bsize;

        /* data copy */
        if (*length) 
        {
            memcpy(buffer, l->data, *length);
            r = 0;
        }
    }
    return r;
}

/* remove */
int
store_remove(unsigned key)
{
    LOG* p = NULL;
    LOG* l;
    int  idx;
    int  r = -1;

    /* check hash table */
    idx = key % HASHSIZE;
    l   = ghashtbl[idx];
    while (l)
    {
        /* traverse linked list of hash entry */

        /* found --> delete */
        if (l->key == key) 
        {
            if (p) p->next       = l->next;
            else   ghashtbl[idx] = l->next;

            /* memory free */
            free(l);

            r = 0;
            break;
        }
        p = l;
        l = l->next;
    }
    return r;
}

/* free everything */
void 
store_free(void)
{
    LOG* next;
    LOG* l;
    int  idx;
   
    for (idx = 0; idx < HASHSIZE; idx++)
    {
        l = ghashtbl[idx];
        while (l)
        {
            next = l->next;
            free(l);
            l    = next;
        }
    }
}
